package com.jltx.xq.zeus.startup.sort

import com.jltx.xq.zeus.startup.Startup
import com.jltx.xq.zeus.startup.execption.StartupException
import com.jltx.xq.zeus.startup.model.StartupSortStore
import com.jltx.xq.zeus.startup.utils.StartupLogUtils
import com.jltx.xq.zeus.startup.ext.getUniqueKey
import java.util.*

/**
 * @author aidenquan
 *         Created by junlintianxia on 2022/07/15.
 */
internal object TopologySort {

    fun sort(startupList: List<Startup<*>>): StartupSortStore {


        //记录任务入度数的表
        val inDegreeMap = hashMapOf<String, Int>()
        //0度表
        val zeroDeque = ArrayDeque<String>()
        //任务表
        val startupMap = hashMapOf<String, Startup<*>>()
        //每个任务所依赖的任务依赖表
        val startupChildrenMap = hashMapOf<String, MutableList<String>>()
        //1.找出图中入度数为0的节点   填表的过程
        startupList.forEach {
            val uniqueKey = it::class.java.getUniqueKey()
            if (!startupMap.containsKey(uniqueKey)) {
                startupMap[uniqueKey] = it
                inDegreeMap[uniqueKey] = it.getDependenciesCount()
                //记录入度数（依赖的任务数）为0的任务
                if (inDegreeMap[uniqueKey] == 0) {
                    zeroDeque.offer(uniqueKey)
                } else {
                    it.dependenciesByName()?.forEach { parentClsName ->
                        val parentUniqueKey = parentClsName.getUniqueKey()
                        if (startupChildrenMap[parentUniqueKey] == null) {
                            //记录这个父任务的所有子任务
                            startupChildrenMap[parentUniqueKey] = arrayListOf()
                        }
                        //记录这个父任务的所有子任务
                        startupChildrenMap[parentUniqueKey]?.add(uniqueKey)
                    }
                }
            } else {
                throw StartupException("$it multiple add.")
            }
        }

        //2.删除图中入度为0的这些顶点,并更新全图，，最后完成排序
        /**
         * 主线程任务表
         */
        val mainResult = mutableListOf<Startup<*>>()

        /**
         * 非主线程任务表
         */
        val ioResult = mutableListOf<Startup<*>>()

        //初次没有进行线程先后优化的拓扑排序
        val temp = mutableListOf<Startup<*>>()

        //处理入度为0的任务
        while (!zeroDeque.isEmpty()) {
            zeroDeque.poll()?.let {
                startupMap[it]?.apply {
                    //把入度数为0的任务添加到tmpList
                    temp.add(this)

                    //根据是否在主线程执行的任务进行主线程和子线程的分开存储，为了防止主线程执行时在队列里面阻塞子线程任务的执行，优先把子线程任务先放到拓扑排序队列的前面
                    if (this.callCreateOnMainThread()) {
                        mainResult.add(this)
                    } else {
                        ioResult.add(this)
                    }

                }

                startupChildrenMap[it]?.forEach { children ->
                    //完成一个子任务所依赖的入读数减1
                    inDegreeMap[children] = inDegreeMap[children]?.minus(1) ?: 0
                    //当子任务入度数为0，添加到0入度数队列里面
                    if (inDegreeMap[children] == 0) {
                        zeroDeque.offer(children)
                    }
                }
            }
        }

        if (mainResult.size + ioResult.size != startupList.size) {
            throw StartupException("lack of dependencies or have circle dependencies.")
        }
        printResult(temp)
        val result = ioResult.apply {
            addAll(mainResult)
        }
        printResult(result)
        return StartupSortStore(result, startupMap, startupChildrenMap)
    }

    private fun printResult(result: List<Startup<*>>) {
        val printBuilder = buildString {
            append("TopologySort result: ")
            append("\n")
            append("|================================================================")
            result.forEachIndexed { index, it ->
                append("\n")
                append("|         order          |    [${index + 1}] ")
                append("\n")
                append("|----------------------------------------------------------------")
                append("\n")
                append("|        Startup         |    ${it::class.java.simpleName}")
                append("\n")
                append("|----------------------------------------------------------------")
                append("\n")
                append("|   Dependencies size    |    ${it.getDependenciesCount()}")
                append("\n")
                append("|----------------------------------------------------------------")
                append("\n")
                append("| callCreateOnMainThread |    ${it.callCreateOnMainThread()}")
                append("\n")
                append("|----------------------------------------------------------------")
                append("\n")
                append("|    waitOnMainThread    |    ${it.waitOnMainThread()}")
                append("\n")
                append("|================================================================")
            }
        }
        StartupLogUtils.d { printBuilder }
    }
}